home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 8: LINUX Games / Linux Cubed Series 8 - LINUX Games.iso / games / x11 / strategy / xpuzzles.3 / xpuzzles / xpuzzles-5.3.1 / xmlink / Mlink.c < prev    next >
C/C++ Source or Header  |  1996-03-07  |  40KB  |  1,357 lines

  1. /*
  2. # X-BASED MISSING LINK(tm)
  3. #
  4. #  Mlink.c
  5. #
  6. ###
  7. #
  8. #  Copyright (c) 1994 - 96    David Albert Bagley, bagleyd@hertz.njit.edu
  9. #
  10. #                   All Rights Reserved
  11. #
  12. #  Permission to use, copy, modify, and distribute this software and
  13. #  its documentation for any purpose and without fee is hereby granted,
  14. #  provided that the above copyright notice appear in all copies and
  15. #  that both that copyright notice and this permission notice appear in
  16. #  supporting documentation, and that the name of the author not be
  17. #  used in advertising or publicity pertaining to distribution of the
  18. #  software without specific, written prior permission.
  19. #
  20. #  This program is distributed in the hope that it will be "playable",
  21. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  23. #
  24. */
  25.  
  26. /* Methods file for Mlink */
  27.  
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #ifdef VMS
  31. #include <unixlib.h>
  32. #else
  33. #ifndef apollo
  34. #include <unistd.h>
  35. #endif
  36. #endif
  37. #include <X11/IntrinsicP.h>
  38. #include <X11/Intrinsic.h>
  39. #include <X11/StringDefs.h>
  40. #include <X11/CoreP.h>
  41. #include "MlinkP.h"
  42.  
  43. #ifndef DATAFILE
  44. #define DATAFILE "/usr/games/lib/mlink.data"
  45. #endif
  46.  
  47. static void InitializeMlink();
  48. static void ExposeMlink();
  49. static void ResizeMlink();
  50. static void DestroyMlink();
  51. static Boolean SetValuesMlink();
  52. static void QuitMlink();
  53. static void MoveMlinkTop();
  54. static void MoveMlinkLeft();
  55. static void MoveMlinkRight();
  56. static void MoveMlinkBottom();
  57. static void MoveMlinkInput();
  58. static void SelectMlink();
  59. static void ReleaseMlink();
  60. static void RandomizeMlink();
  61. static void RandomizeMlinkMaybe();
  62. static void GetMlink();
  63. static void WriteMlink();
  64. static void UndoMlink();
  65. static void SolveMlink();
  66. static void OrientizeMlink();
  67. static void MiddleMlink();
  68. static void ControlMlink();
  69. static void RotateMlink();
  70. static void SlideMlink();
  71. static int SelectTiles();
  72. static void SelectSlideTiles();
  73. static void GetColor();
  74. static void MoveShiftCb();
  75. static void CheckTiles();
  76. static void ResetTiles();
  77. static void ResizeTiles();
  78. static void MoveNoTiles();
  79. static int MoveTilesDir();
  80. static void RandomizeTiles();
  81. static void MoveTiles();
  82. static void RotateTiles();
  83. static int ExchangeTiles();
  84. static void DrawFrame();
  85. static void DrawTile();
  86. static void DrawLink();
  87. static int PositionTile();
  88. static int Row();
  89. static int Column();
  90.  
  91. static char defaultTranslationsMlink[] =
  92.   "<KeyPress>q: Quit()\n\
  93.    Ctrl<KeyPress>C: Quit()\n\
  94.    <KeyPress>Up: MoveTop()\n\
  95.    <KeyPress>KP_8: MoveTop()\n\
  96.    <KeyPress>R8: MoveTop()\n\
  97.    <KeyPress>Left: MoveLeft()\n\
  98.    <KeyPress>KP_4: MoveLeft()\n\
  99.    <KeyPress>R10: MoveLeft()\n\
  100.    <KeyPress>Right: MoveRight()\n\
  101.    <KeyPress>KP_6: MoveRight()\n\
  102.    <KeyPress>R12: MoveRight()\n\
  103.    <KeyPress>Down: MoveBottom()\n\
  104.    <KeyPress>KP_2: MoveBottom()\n\
  105.    <KeyPress>R14: MoveBottom()\n\
  106.    <Btn1Down>: Select()\n\
  107.    <Btn1Up>: Release()\n\
  108.    <KeyPress>r: Randomize()\n\
  109.    <Btn3Down>(2+): Randomize()\n\
  110.    <Btn3Down>: RandomizeMaybe()\n\
  111.    <KeyPress>g: Get()\n\
  112.    <KeyPress>w: Write()\n\
  113.    <KeyPress>u: Undo()\n\
  114.    <KeyPress>s: Solve()\n\
  115.    <KeyPress>o: Orientize()\n\
  116.    <KeyPress>m: Middle()";
  117.  
  118. static XtActionsRec actionsListMlink[] =
  119. {
  120.   {"Quit", (XtActionProc) QuitMlink},
  121.   {"MoveTop", (XtActionProc) MoveMlinkTop},
  122.   {"MoveLeft", (XtActionProc) MoveMlinkLeft},
  123.   {"MoveRight", (XtActionProc) MoveMlinkRight},
  124.   {"MoveBottom", (XtActionProc) MoveMlinkBottom},
  125.   {"Select", (XtActionProc) SelectMlink},
  126.   {"Release", (XtActionProc) ReleaseMlink},
  127.   {"Randomize", (XtActionProc) RandomizeMlink},
  128.   {"RandomizeMaybe", (XtActionProc) RandomizeMlinkMaybe},
  129.   {"Get", (XtActionProc) GetMlink},
  130.   {"Write", (XtActionProc) WriteMlink},
  131.   {"Undo", (XtActionProc) UndoMlink},
  132.   {"Solve", (XtActionProc) SolveMlink},
  133.   {"Orientize", (XtActionProc) OrientizeMlink},
  134.   {"Middle", (XtActionProc) MiddleMlink}
  135. };
  136.  
  137. static XtResource resourcesMlink[] =
  138. {
  139.   {XtNfaceColor0, XtCLabel, XtRString, sizeof(String),
  140.    XtOffset(MlinkWidget, mlink.faceName[0]), XtRString, "White"},
  141.   {XtNfaceColor1, XtCLabel, XtRString, sizeof(String),
  142.    XtOffset(MlinkWidget, mlink.faceName[1]), XtRString, "Yellow"},
  143.   {XtNfaceColor2, XtCLabel, XtRString, sizeof(String),
  144.    XtOffset(MlinkWidget, mlink.faceName[2]), XtRString, "Green"},
  145.   {XtNfaceColor3, XtCLabel, XtRString, sizeof(String),
  146.    XtOffset(MlinkWidget, mlink.faceName[3]), XtRString, "Red"},
  147.   {XtNfaceColor4, XtCLabel, XtRString, sizeof(String),
  148.    XtOffset(MlinkWidget, mlink.faceName[4]), XtRString, "Blue"},
  149.   {XtNfaceColor5, XtCLabel, XtRString, sizeof(String),
  150.    XtOffset(MlinkWidget, mlink.faceName[5]), XtRString, "Magenta"},
  151.   {XtNfaceColor6, XtCLabel, XtRString, sizeof(String),
  152.    XtOffset(MlinkWidget, mlink.faceName[6]), XtRString, "Cyan"},
  153.   {XtNfaceColor7, XtCLabel, XtRString, sizeof(String),
  154.    XtOffset(MlinkWidget, mlink.faceName[7]), XtRString, "Orange"},
  155.   {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
  156.    XtOffset(MlinkWidget, mlink.foreground), XtRString, XtDefaultBackground},
  157.   {XtNtileColor, XtCColor, XtRPixel, sizeof(Pixel),
  158.    XtOffset(MlinkWidget, mlink.tileColor), XtRString, XtDefaultForeground},
  159.   {XtNtileBorder, XtCColor, XtRPixel, sizeof(Pixel),
  160.    XtOffset(MlinkWidget, mlink.borderColor), XtRString, XtDefaultForeground},
  161.   {XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension),
  162.    XtOffset(MlinkWidget, core.width), XtRString, "200"},
  163.   {XtNheight, XtCHeight, XtRDimension, sizeof(Dimension),
  164.    XtOffset(MlinkWidget, core.height), XtRString, "200"},
  165.   {XtNtiles, XtCTiles, XtRInt, sizeof(int),
  166.    XtOffset(MlinkWidget, mlink.tiles), XtRString, "4"}, /* DEFAULTTILES */
  167.   {XtNfaces, XtCFaces, XtRInt, sizeof(int),
  168.    XtOffset(MlinkWidget, mlink.faces), XtRString, "4"}, /* DEFAULTFACES */
  169.   {XtNorient, XtCOrient, XtRBoolean, sizeof(Boolean),
  170.    XtOffset(MlinkWidget, mlink.orient), XtRString, "FALSE"}, /*DEFAULTORIENT*/
  171.   {XtNmiddle, XtCMiddle, XtRBoolean, sizeof(Boolean),
  172.    XtOffset(MlinkWidget, mlink.middle), XtRString,
  173.    "TRUE"}, /*DEFAULTMIDDLE*/
  174.   {XtNmono, XtCMono, XtRBoolean, sizeof(Boolean),
  175.    XtOffset(MlinkWidget, mlink.mono), XtRString, "FALSE"},
  176.   {XtNbase, XtCBase, XtRInt, sizeof(int),
  177.    XtOffset(MlinkWidget, mlink.base), XtRString, "10"},
  178.   {XtNstart, XtCBoolean, XtRBoolean, sizeof(Boolean),
  179.    XtOffset(MlinkWidget, mlink.started), XtRString, "FALSE"},
  180.   {XtNselectCallback, XtCCallback, XtRCallback, sizeof(caddr_t),
  181.    XtOffset(MlinkWidget, mlink.select), XtRCallback, NULL}
  182. };
  183.  
  184. MlinkClassRec mlinkClassRec =
  185. {
  186.   {
  187.     (WidgetClass) &widgetClassRec,    /* superclass */
  188.     "Mlink",                /* class name */
  189.     sizeof(MlinkRec),            /* widget size */
  190.     NULL,                /* class initialize */
  191.     NULL,                /* class part initialize */
  192.     FALSE,                /* class inited */
  193.     InitializeMlink,            /* initialize */
  194.     NULL,                /* initialize hook */
  195.     XtInheritRealize,            /* realize */
  196.     actionsListMlink,            /* actions */
  197.     XtNumber(actionsListMlink),        /* num actions */
  198.     resourcesMlink,            /* resources */
  199.     XtNumber(resourcesMlink),        /* num resources */
  200.     NULLQUARK,                /* xrm class */
  201.     TRUE,                /* compress motion */
  202.     TRUE,                /* compress exposure */
  203.     TRUE,                /* compress enterleave */
  204.     TRUE,                /* visible interest */
  205.     DestroyMlink,            /* destroy */
  206.     ResizeMlink,            /* resize */
  207.     ExposeMlink,            /* expose */
  208.     SetValuesMlink,            /* set values */
  209.     NULL,                /* set values hook */
  210.     XtInheritSetValuesAlmost,        /* set values almost */
  211.     NULL,                /* get values hook */
  212.     NULL,                /* accept focus */
  213.     XtVersion,                /* version */
  214.     NULL,                /* callback private */
  215.     defaultTranslationsMlink,        /* tm table */
  216.     NULL,                /* query geometry */
  217.     NULL,                /* display accelerator */
  218.     NULL                /* extension */
  219.   },
  220.   {
  221.     0                    /* ignore */
  222.   }
  223. };
  224.  
  225. WidgetClass mlinkWidgetClass = (WidgetClass) &mlinkClassRec;
  226.  
  227. static void InitializeMlink(request, new)
  228.   Widget request, new;
  229. {
  230.   MlinkWidget w = (MlinkWidget) new;
  231.   XGCValues values;
  232.   XtGCMask valueMask;
  233.   int face;
  234.  
  235.   w->mlink.tileOfPosition = NULL;
  236.   CheckTiles(w);
  237.   InitMoves();
  238.   ResetTiles(w);
  239.   (void) SRAND(getpid());
  240.   valueMask = GCForeground | GCBackground;
  241.   values.foreground = w->mlink.foreground;
  242.   values.background = w->core.background_pixel;
  243.   w->mlink.puzzleGC = XtGetGC(new, valueMask, &values);
  244.   w->mlink.depth = DefaultDepthOfScreen(XtScreen(w));
  245.   values.foreground = w->mlink.tileColor;
  246.   w->mlink.tileGC = XtGetGC(new, valueMask, &values);
  247.   values.foreground = w->mlink.borderColor;
  248.   w->mlink.borderGC = XtGetGC(new, valueMask, &values);
  249.   valueMask = GCForeground | GCBackground;
  250.   values.foreground = w->core.background_pixel;
  251.   values.background = w->mlink.foreground;
  252.   w->mlink.inverseGC = XtGetGC(new, valueMask, &values);
  253.   for (face = 0; face < MAXFACES; face++)
  254.     GetColor(w, face, TRUE);
  255.   ResizeMlink(w);
  256. }
  257.  
  258. static void DestroyMlink(old)
  259.   Widget old;
  260. {
  261.   MlinkWidget w = (MlinkWidget) old;
  262.   int face;
  263.  
  264.   for (face = 0; face < MAXFACES; face++)
  265.     XtReleaseGC(old, w->mlink.faceGC[face]);
  266.   XtReleaseGC(old, w->mlink.tileGC);
  267.   XtReleaseGC(old, w->mlink.puzzleGC);
  268.   XtReleaseGC(old, w->mlink.inverseGC);
  269.   XtRemoveCallbacks(old, XtNselectCallback, w->mlink.select);
  270. }
  271.  
  272. static void ResizeMlink(w)
  273.   MlinkWidget w;
  274. {
  275.   w->mlink.delta.x = 3;
  276.   w->mlink.delta.y = 3;
  277.   w->mlink.offset.x = MAX(((int) w->core.width - w->mlink.delta.x) /
  278.     w->mlink.tiles, 0);
  279.   w->mlink.offset.y = MAX(((int) w->core.height - w->mlink.delta.y) /
  280.     w->mlink.faces, 0);
  281.   w->mlink.faceSize.x = w->mlink.offset.x * w->mlink.tiles +
  282.     w->mlink.delta.x;
  283.   w->mlink.faceSize.y = w->mlink.offset.y + w->mlink.delta.y;
  284.   w->mlink.puzzleSize.x = w->mlink.faceSize.x + w->mlink.delta.x;
  285.   w->mlink.puzzleSize.y = (w->mlink.faceSize.y - w->mlink.delta.y) *
  286.     w->mlink.faces + w->mlink.delta.y;
  287.   w->mlink.puzzleOffset.x = ((int) w->core.width -
  288.     w->mlink.puzzleSize.x + 2) / 2;
  289.   w->mlink.puzzleOffset.y = ((int) w->core.height -
  290.     w->mlink.puzzleSize.y + 2) / 2;
  291.   w->mlink.tileSize.x = MAX(w->mlink.offset.x - w->mlink.delta.x, 0);
  292.   w->mlink.tileSize.y = MAX(w->mlink.offset.y - w->mlink.delta.y , 0);
  293.   ResizeTiles(w);
  294. }
  295.  
  296. static void ExposeMlink(new, event, region)
  297.   Widget new;
  298.   XEvent *event;
  299.   Region region; /* Not used */
  300. {
  301.   MlinkWidget w = (MlinkWidget) new;
  302.  
  303.   if (w->core.visible) {
  304.     DrawFrame(w, w->mlink.puzzleGC);
  305.     DrawAllTiles(w, w->mlink.tileGC, w->mlink.borderGC);
  306.   }
  307. }
  308.  
  309. static Boolean SetValuesMlink(current, request, new)
  310.   Widget current, request, new;
  311. {
  312.   MlinkWidget c = (MlinkWidget) current, w = (MlinkWidget) new;
  313.   XGCValues values;
  314.   XtGCMask valueMask;
  315.   Boolean redraw = FALSE;
  316.   Boolean redrawTiles = FALSE;
  317.   int face;
  318.  
  319.   CheckTiles(w);
  320.   if (w->mlink.foreground != c->mlink.foreground) {
  321.     valueMask = GCForeground | GCBackground;
  322.     values.foreground = w->mlink.foreground;
  323.     values.background = w->core.background_pixel;
  324.     XtReleaseGC(new, w->mlink.puzzleGC);
  325.     w->mlink.puzzleGC = XtGetGC(new, valueMask, &values);
  326.     if (w->mlink.mono || w->mlink.depth == 1) {
  327.       values.foreground = w->core.background_pixel;
  328.       values.background = w->mlink.tileColor;
  329.       for (face = 0; face < MAXFACES; face++) {
  330.         XtReleaseGC((Widget) w, w->mlink.faceGC[face]);
  331.         w->mlink.faceGC[face] = XtGetGC((Widget) w, valueMask, &values);
  332.       }
  333.       c->mlink.mono = w->mlink.mono;
  334.     }
  335.     redrawTiles = TRUE;
  336.   }
  337.   if (w->core.background_pixel != c->core.background_pixel) {
  338.     valueMask = GCForeground | GCBackground;
  339.     values.foreground = w->core.background_pixel;
  340.     values.background = w->mlink.foreground;
  341.     XtReleaseGC(new, w->mlink.inverseGC);
  342.     w->mlink.inverseGC = XtGetGC(new, valueMask, &values);
  343.     redrawTiles = TRUE;
  344.   }
  345.   if (w->mlink.tileColor != c->mlink.tileColor) {
  346.     valueMask = GCForeground | GCBackground;
  347.     values.foreground = w->mlink.tileColor;
  348.     values.background = w->core.background_pixel;
  349.     XtReleaseGC(new, w->mlink.tileGC);
  350.     w->mlink.tileGC = XtGetGC(new, valueMask, &values);
  351.     redrawTiles = TRUE;
  352.   }
  353.   if (w->mlink.borderColor != c->mlink.borderColor) {
  354.     valueMask = GCForeground | GCBackground;
  355.     values.foreground = w->mlink.borderColor;
  356.     values.background = w->core.background_pixel;
  357.     XtReleaseGC(new, w->mlink.borderGC);
  358.     w->mlink.borderGC = XtGetGC(new, valueMask, &values);
  359.     redrawTiles = TRUE;
  360.   }
  361.   for (face = 0; face < MAXFACES; face++) {
  362.     if (strcmp(w->mlink.faceName[face], c->mlink.faceName[face]))
  363.       GetColor(w, face, FALSE);
  364.   }
  365.   if (w->mlink.tiles != c->mlink.tiles ||
  366.       w->mlink.faces != c->mlink.faces ||
  367.       w->mlink.orient != c->mlink.orient ||
  368.       w->mlink.middle != c->mlink.middle ||
  369.       w->mlink.base != c->mlink.base) {
  370.     ResetTiles(w);
  371.     ResizeMlink(w);
  372.     redraw = TRUE;
  373.   }
  374.   else if (w->mlink.offset.x != c->mlink.offset.x ||
  375.            w->mlink.offset.y != c->mlink.offset.y) {
  376.     ResizeMlink(w);
  377.     redraw = TRUE;
  378.   }
  379.   if (redrawTiles && !redraw && XtIsRealized(new) && new->core.visible) {
  380.     DrawFrame(w, c->mlink.inverseGC);
  381.     DrawAllTiles(c, c->mlink.inverseGC, c->mlink.inverseGC);
  382.     DrawFrame(w, w->mlink.puzzleGC);
  383.     DrawAllTiles(w, c->mlink.tileGC, c->mlink.borderGC);
  384.   }
  385.   return (redraw);
  386. }
  387.  
  388. static void QuitMlink(w, event, args, nArgs)
  389.   MlinkWidget w;
  390.   XEvent *event;
  391.   char *args[];
  392.   int nArgs;
  393. {
  394.   XtCloseDisplay(XtDisplay(w));
  395.   exit(0);
  396. }
  397.  
  398. static void SelectMlink(w, event, args, nArgs)
  399.   MlinkWidget w;
  400.   XEvent *event;
  401.   char *args[];
  402.   int nArgs;
  403. {
  404.   int i, j, pos, shift;
  405.  
  406.   pos = SelectTiles(w, event->xbutton.x, event->xbutton.y, &i, &j);
  407.   if (-w->mlink.tileFaces != pos) {
  408.     w->mlink.currentTile = i;
  409.     w->mlink.currentFace = j;
  410.     w->mlink.currentRef = pos;
  411.     shift = (int) (event->xkey.state & (ShiftMask | LockMask));
  412.     if (shift || !CheckSolved(w)) {
  413.       pos = w->mlink.currentTile + w->mlink.currentFace * w->mlink.tiles;
  414.       if (w->mlink.tileOfPosition[i + j * w->mlink.tiles] <= 0)
  415.         DrawTile(w, w->mlink.borderGC, w->mlink.borderGC, pos, TRUE);
  416.       else
  417.         DrawTile(w, w->mlink.borderGC, w->mlink.tileGC, pos, TRUE);
  418.     }
  419.   } else
  420.     w->mlink.currentRef = -w->mlink.tileFaces;
  421. }
  422.  
  423. static void ReleaseMlink(w, event, args, nArgs)
  424.   MlinkWidget w;
  425.   XEvent *event;
  426.   char *args[];
  427.   int nArgs;
  428. {
  429.   int i, j, pos, diff, shift;
  430.  
  431.   if (w->mlink.currentRef == -w->mlink.tileFaces)
  432.     return;
  433.   pos = w->mlink.currentTile + w->mlink.currentFace * w->mlink.tiles;
  434.   DrawTile(w, w->mlink.inverseGC, w->mlink.inverseGC, pos, TRUE);
  435.   if (w->mlink.tileOfPosition[pos] > 0)
  436.     DrawTile(w, w->mlink.tileGC, w->mlink.borderGC, pos, FALSE);
  437.   shift = (int) (event->xkey.state & (ShiftMask | LockMask));
  438.   if (!shift && CheckSolved(w))
  439.     MoveNoTiles(w);
  440.   else {
  441.     pos = SelectTiles(w, event->xbutton.x, event->xbutton.y, &i, &j);
  442.     if (-w->mlink.tileFaces != pos) {
  443.       if (j == w->mlink.currentFace) {
  444.         pos = w->mlink.currentRef;
  445.         if (pos / w->mlink.tiles == 0 &&
  446.             j == Column(w, w->mlink.spacePosition) && pos != 0) {
  447.           if (shift && CheckSolved(w))
  448.             MoveNoTiles(w);
  449.           else {
  450.             SelectSlideTiles(w, pos);
  451.             w->mlink.currentTile = w->mlink.tiles;
  452.             if (CheckSolved(w)) {
  453.               mlinkCallbackStruct cb;
  454.  
  455.               cb.reason = MLINK_SOLVED;
  456.               XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  457.             }
  458.           }
  459.         }
  460.       } else {
  461.         diff = (w->mlink.currentFace - j + w->mlink.faces) % w->mlink.faces;
  462.         if (diff > w->mlink.faces / 2)
  463.           for (i = 0; i < w->mlink.faces - diff; i++)
  464.             (void) MoveMlink(w, BOTTOM, w->mlink.currentTile, shift);
  465.         else
  466.           for (i = 0; i < diff; i++)
  467.             (void) MoveMlink(w, TOP, w->mlink.currentTile, shift);
  468.         if (!shift && CheckSolved(w)) {
  469.           mlinkCallbackStruct cb;
  470.  
  471.           cb.reason = MLINK_SOLVED;
  472.           XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  473.         }
  474.       }
  475.     }
  476.   }
  477. }
  478.  
  479. static void RandomizeMlink(w, event, args, nArgs)
  480.   MlinkWidget w;
  481.   XEvent *event;
  482.   char *args[];
  483.   int nArgs;
  484. {
  485.   RandomizeTiles(w);
  486. }
  487.  
  488. static void RandomizeMlinkMaybe(w, event, args, nArgs)
  489.   MlinkWidget w;
  490.   XEvent *event;
  491.   char *args[];
  492.   int nArgs;
  493. {
  494.   if (!w->mlink.started)
  495.     RandomizeTiles(w);
  496. }
  497.  
  498. static void GetMlink(w, event, args, nArgs)
  499.   MlinkWidget w;
  500.   XEvent *event;
  501.   char *args[];
  502.   int nArgs;
  503. {
  504.   FILE *fp;
  505.   char c;
  506.   int i, tiles, faces, middle, orient, moves;
  507.   mlinkCallbackStruct cb;
  508.  
  509.   if ((fp = fopen(DATAFILE, "r")) == NULL)
  510.     (void) printf("Can not read %s for get.\n", DATAFILE);
  511.   else {
  512.     FlushMoves(w);
  513.     while ((c = getc(fp)) != EOF && c != SYMBOL);
  514.     (void) fscanf(fp, "%d", &tiles);
  515.     if (tiles >= MINTILES) {
  516.       for (i = w->mlink.tiles; i < tiles; i++) {
  517.         cb.reason = MLINK_INC_X;
  518.         XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  519.       }
  520.       for (i = w->mlink.tiles; i > tiles; i--) {
  521.         cb.reason = MLINK_DEC_X;
  522.         XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  523.       }
  524.     } else
  525.       (void) printf("%s corrupted: tiles %d should be between %d and MAXINT\n",
  526.          DATAFILE, tiles, MINTILES);
  527.     while ((c = getc(fp)) != EOF && c != SYMBOL);
  528.     (void) fscanf(fp, "%d", &faces);
  529.     if (faces >= MINFACES && faces <= MAXFACES) {
  530.       for (i = w->mlink.faces; i < faces; i++) {
  531.         cb.reason = MLINK_INC_Y;
  532.         XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  533.       }
  534.       for (i = w->mlink.faces; i > faces; i--) {
  535.         cb.reason = MLINK_DEC_Y;
  536.         XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  537.       }
  538.     } else
  539.       (void) printf("%s corrupted: faces %d should be between %d and %d\n",
  540.          DATAFILE, faces, MINFACES, MAXFACES);
  541.     while ((c = getc(fp)) != EOF && c != SYMBOL);
  542.     (void) fscanf(fp, "%d", &middle);
  543.     if (w->mlink.middle != (Boolean) middle) {
  544.       cb.reason = MLINK_MIDDLE;
  545.       XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  546.     }
  547.     while ((c = getc(fp)) != EOF && c != SYMBOL);
  548.     (void) fscanf(fp, "%d", &orient);
  549.     if (w->mlink.orient != (Boolean) orient) {
  550.       cb.reason = MLINK_ORIENT;
  551.       XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  552.     }
  553.     while ((c = getc(fp)) != EOF && c != SYMBOL);
  554.     (void) fscanf(fp, "%d", &moves);
  555.     ScanStartPosition(fp, w);
  556.     cb.reason = MLINK_RESTORE;
  557.     XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  558.     SetStartPosition(w);
  559.     ScanMoves(fp, w, moves);
  560.     (void) fclose(fp);
  561.     (void) printf("%s: tiles %d, faces %d, middle %d, orient %d, moves %d.\n",
  562.       DATAFILE, tiles, faces, middle, orient, moves);
  563.   }
  564. }
  565.  
  566. static void WriteMlink(w, event, args, nArgs)
  567.   MlinkWidget w;
  568.   XEvent *event;
  569.   char *args[];
  570.   int nArgs;
  571. {
  572.   FILE *fp;
  573.  
  574.   if ((fp = fopen(DATAFILE, "w")) == NULL)
  575.     (void) printf("Can not write to %s.\n", DATAFILE);
  576.   else {
  577.     (void) fprintf(fp, "tiles%c %d\n", SYMBOL, w->mlink.tiles);
  578.     (void) fprintf(fp, "faces%c %d\n", SYMBOL, w->mlink.faces);
  579.     (void) fprintf(fp, "middle%c %d\n", SYMBOL, (w->mlink.middle) ? 1 : 0);
  580.     (void) fprintf(fp, "orient%c %d\n", SYMBOL, (w->mlink.orient) ? 1 : 0);
  581.     (void) fprintf(fp, "moves%c %d\n", SYMBOL, NumMoves());
  582.     PrintStartPosition(fp, w);
  583.     PrintMoves(fp);
  584.     (void) fclose(fp);
  585.     (void) printf("Saved to %s.\n", DATAFILE);
  586.   }
  587. }
  588.  
  589. static void UndoMlink(w, event, args, nArgs)
  590.   MlinkWidget w;
  591.   XEvent *event;
  592.   char *args[];
  593.   int nArgs;
  594. {
  595.   if (MadeMoves()) {
  596.     int direction, tile, shift;
  597.     mlinkCallbackStruct cb;
  598.  
  599.     GetMove(&direction, &tile, &shift);
  600.     direction = (direction + COORD / 2) % COORD;
  601.     if (shift && (direction == TOP || direction == BOTTOM))
  602.       MoveShiftCb(w, direction);
  603.     else if (MoveTilesDir(w, direction, tile)) {
  604.       cb.reason = MLINK_UNDO;
  605.       XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  606.     }
  607.   }
  608. }
  609.  
  610. static void SolveMlink(w, event, args, nArgs)
  611.   MlinkWidget w;
  612.   XEvent *event;
  613.   char *args[];
  614.   int nArgs;
  615. {
  616.   /* SolveTiles(w); */ /* Sorry, unimplemented */
  617. }
  618.  
  619. static void OrientizeMlink(w, event, args, nArgs)
  620.   MlinkWidget w;
  621.   XEvent *event;
  622.   char *args[];
  623.   int nArgs;
  624. {
  625.   mlinkCallbackStruct cb;
  626.  
  627.   cb.reason = MLINK_ORIENT;
  628.   XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  629. }
  630.  
  631. static void MiddleMlink(w, event, args, nArgs)
  632.   MlinkWidget w;
  633.   XEvent *event;
  634.   char *args[];
  635.   int nArgs;
  636. {
  637.   mlinkCallbackStruct cb;
  638.  
  639.   cb.reason = MLINK_MIDDLE;
  640.   XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  641. }
  642.  
  643. static void MoveMlinkTop(w, event, args, nArgs)
  644.   MlinkWidget w;
  645.   XEvent *event;
  646.   char *args[];
  647.   int nArgs;
  648. {
  649.   MoveMlinkInput(w, event->xbutton.x, TOP,
  650.     (int) (event->xkey.state & (ShiftMask | LockMask)),
  651.     (int) (event->xkey.state & ControlMask));
  652. }
  653.  
  654. static void MoveMlinkLeft(w, event, args, nArgs)
  655.   MlinkWidget w;
  656.   XEvent *event;
  657.   char *args[];
  658.   int nArgs;
  659. {
  660.   MoveMlinkInput(w, event->xbutton.x, LEFT,
  661.     (int) (event->xkey.state & (ShiftMask | LockMask)),
  662.     (int) (event->xkey.state & ControlMask));
  663. }
  664.  
  665. static void MoveMlinkRight(w, event, args, nArgs)
  666.   MlinkWidget w;
  667.   XEvent *event;
  668.   char *args[];
  669.   int nArgs;
  670. {
  671.   MoveMlinkInput(w, event->xbutton.x, RIGHT,
  672.     (int) (event->xkey.state & (ShiftMask | LockMask)),
  673.     (int) (event->xkey.state & ControlMask));
  674. }
  675.  
  676. static void MoveMlinkBottom(w, event, args, nArgs)
  677.   MlinkWidget w;
  678.   XEvent *event;
  679.   char *args[];
  680.   int nArgs;
  681. {
  682.   MoveMlinkInput(w, event->xbutton.x, BOTTOM,
  683.     (int) (event->xkey.state & (ShiftMask | LockMask)),
  684.     (int) (event->xkey.state & ControlMask));
  685. }
  686.  
  687. static void MoveMlinkInput(w, x, direction, shift, control)
  688.   MlinkWidget w;
  689.   int x, direction, shift, control;
  690. {
  691.   mlinkCallbackStruct cb;
  692.   int r;
  693.  
  694.   if (control)
  695.     ControlMlink(w, direction);
  696.   else if (shift && (direction == TOP || direction == BOTTOM)) {
  697.     MoveShiftCb(w, direction);
  698.     PutMove(direction, 0, 1);
  699.   } else if (CheckSolved(w)) {
  700.     MoveNoTiles(w);
  701.     return;
  702.   } else if (direction == LEFT || direction == RIGHT) {
  703.     SlideMlink(w, direction);
  704.     if (CheckSolved(w)) {
  705.       cb.reason = MLINK_SOLVED;
  706.       XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  707.     }
  708.   } else {
  709.     if (!PositionTile(w, x, &r))
  710.       return;
  711.     RotateMlink(w, direction, r);
  712.     if (CheckSolved(w)) {
  713.       cb.reason = MLINK_SOLVED;
  714.       XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  715.     }
  716.   }
  717. }
  718.  
  719. int MoveMlink(w, direction, tile, shift)
  720.   MlinkWidget w;
  721.   int direction, tile, shift;
  722. {
  723.   mlinkCallbackStruct cb;
  724.  
  725.   if (shift && (direction == TOP || direction == BOTTOM)) {
  726.     MoveShiftCb(w, direction);
  727.     return TRUE;
  728.   } else if (CheckSolved(w)) {
  729.     MoveNoTiles(w);
  730.     return FALSE;
  731.   } else if (MoveTilesDir(w, direction, tile)) {
  732.     cb.reason = MLINK_MOVED;
  733.     XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  734.     PutMove(direction, tile, 0);
  735.     return TRUE;
  736.   }
  737.   return FALSE;
  738. }
  739.  
  740. static void ControlMlink(w, direction)
  741.   MlinkWidget w;
  742.   int direction;
  743. {
  744.   mlinkCallbackStruct cb;
  745.  
  746.   cb.reason = MLINK_IGNORE;
  747.   switch (direction) {
  748.     case TOP:
  749.       if (w->mlink.faces <= MINFACES)
  750.         return;
  751.       cb.reason = MLINK_DEC_Y;
  752.       break;
  753.     case RIGHT:
  754.       cb.reason = MLINK_INC_X;
  755.       break;
  756.     case BOTTOM:
  757.       if (w->mlink.faces >= MAXFACES)
  758.         return;
  759.       cb.reason = MLINK_INC_Y;
  760.       break;
  761.     case LEFT:
  762.       if (w->mlink.tiles <= MINTILES)
  763.         return;
  764.       cb.reason = MLINK_DEC_X;
  765.       break;
  766.     default:
  767.       (void) printf("ControlMlink: direction %d\n", direction);
  768.   }
  769.   XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  770. }
  771.  
  772. static void RotateMlink(w, direction, tile)
  773.   MlinkWidget w;
  774.   int direction, tile;
  775. {
  776.   mlinkCallbackStruct cb;
  777.  
  778.   if (CheckSolved(w)) {
  779.     MoveNoTiles(w);
  780.     return;
  781.   }
  782.   (void) MoveMlink(w, direction, tile, FALSE);
  783.   if (CheckSolved(w)) {
  784.     cb.reason = MLINK_SOLVED;
  785.     XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  786.   }
  787.   return;
  788. }
  789.  
  790. static void SlideMlink(w, direction)
  791.   MlinkWidget w;
  792.   int direction;
  793. {
  794.   mlinkCallbackStruct cb;
  795.  
  796.   if (CheckSolved(w)) {
  797.     MoveNoTiles(w);
  798.     return;
  799.   }
  800.   if (!MoveMlink(w, direction, 0, FALSE)) {
  801.     cb.reason = MLINK_BLOCKED;
  802.     XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  803.     return;
  804.   }
  805.   if (CheckSolved(w)) {
  806.     cb.reason = MLINK_SOLVED;
  807.     XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  808.   }
  809.   return;
  810. }
  811.  
  812. static int SelectTiles(w, x, y, i, j)
  813.   MlinkWidget w;
  814.   int x, y;
  815.   int *i, *j;
  816. {
  817.   *i = (x - w->mlink.delta.x / 2 - w->mlink.puzzleOffset.x) /
  818.     w->mlink.offset.x;
  819.   *j = ((y - w->mlink.delta.y / 2 - w->mlink.puzzleOffset.y) %
  820.     (w->mlink.faces * w->mlink.offset.y + w->mlink.delta.y - 1)) /
  821.     w->mlink.offset.y;
  822.   if (*i >= 0 && *j >= 0 &&
  823.       *i < w->mlink.tiles && *j < w->mlink.faces)
  824.     return (*i + w->mlink.tiles * *j -
  825.       w->mlink.spacePosition % w->mlink.tileFaces);
  826.   return -w->mlink.tileFaces;
  827. }
  828.  
  829. static void SelectSlideTiles(w, pos)
  830.   MlinkWidget w;
  831.   int pos;
  832. {
  833.   mlinkCallbackStruct cb;
  834.   int n;
  835.  
  836.   if (pos < 0) {
  837.     for (n = 1; n <= -pos % w->mlink.tiles; n++) {
  838.       MoveTiles(w, w->mlink.spacePosition - 1);
  839.       cb.reason = MLINK_MOVED;
  840.       XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  841.       PutMove(RIGHT, 0, 0);
  842.     }
  843.   } else if (pos > 0) {
  844.     for (n = 1; n <= pos % w->mlink.tiles; n++) {
  845.       MoveTiles(w, w->mlink.spacePosition + 1);
  846.       cb.reason = MLINK_MOVED;
  847.       XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  848.       PutMove(LEFT, 0, 0);
  849.     }
  850.   }
  851. }
  852.  
  853. static void GetColor(w, face, init)
  854.   MlinkWidget w;
  855.   int face, init;
  856. {
  857.   XGCValues values;
  858.   XtGCMask valueMask;
  859.   XColor colorCell, rgb;
  860.  
  861.   valueMask = GCForeground | GCBackground;
  862.   values.background = w->core.background_pixel;
  863.   if (w->mlink.depth > 1 && !w->mlink.mono) {
  864.     if (XAllocNamedColor(XtDisplay(w),
  865.         DefaultColormap(XtDisplay(w), XtWindow(w)),
  866.         w->mlink.faceName[face], &colorCell, &rgb)) {
  867.       values.foreground = w->mlink.faceColor[face] = colorCell.pixel;
  868.       if (!init)
  869.         XtReleaseGC((Widget) w, w->mlink.faceGC[face]);
  870.       w->mlink.faceGC[face] = XtGetGC((Widget) w, valueMask, &values);
  871.       return;
  872.     } else {
  873.       char buf[121];
  874.  
  875.       (void) sprintf(buf, "Color name \"%s\" is not defined",
  876.                w->mlink.faceName[face]);
  877.       XtWarning(buf);
  878.     }
  879.   }
  880.   values.foreground = w->mlink.foreground;
  881.   if (!init)
  882.     XtReleaseGC((Widget) w, w->mlink.faceGC[face]);
  883.   w->mlink.faceGC[face] = XtGetGC((Widget) w, valueMask, &values);
  884. }
  885.  
  886. static void MoveShiftCb(w, direction)
  887.   MlinkWidget w;
  888.   int direction;
  889. {
  890.   mlinkCallbackStruct cb;
  891.  
  892.   if (w->mlink.middle) {
  893.     (void) MoveTilesDir(w, direction, 0);
  894.     (void) MoveTilesDir(w, direction, 1);
  895.     (void) MoveTilesDir(w, direction, w->mlink.tiles - 1);
  896.   } else {
  897.     int r;
  898.  
  899.     for (r = 0; r < w->mlink.tiles; r++)
  900.       (void) MoveTilesDir(w, direction, r);
  901.   }
  902.   cb.reason = MLINK_CONTROL;
  903.   XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  904. }
  905.  
  906. static void CheckTiles(w)
  907.   MlinkWidget w;
  908. {
  909.   char buf[121];
  910.  
  911.   if (w->mlink.tiles < MINTILES) {
  912.     (void) sprintf(buf,
  913.              "Number of Mlink in X direction out of bounds, use %d..MAXINT",
  914.              MINTILES);
  915.     XtWarning(buf);
  916.     w->mlink.tiles = DEFAULTTILES;
  917.   }
  918.   if (w->mlink.faces < MINFACES || w->mlink.faces > MAXFACES) {
  919.     (void) sprintf(buf,
  920.              "Number of Mlink in Y direction out of bounds, use %d..%d",
  921.              MINFACES, MAXFACES);
  922.     XtWarning(buf);
  923.     w->mlink.faces = DEFAULTFACES;
  924.   }
  925.   w->mlink.base = 10;
  926. }
  927.  
  928. static void ResetTiles(w)
  929.   MlinkWidget w;
  930. {
  931.   int i;
  932.  
  933.   w->mlink.tileFaces = w->mlink.tiles * w->mlink.faces; 
  934.   if (w->mlink.tileOfPosition)
  935.     (void) free((void *) w->mlink.tileOfPosition);
  936.   if (!(w->mlink.tileOfPosition = (int *)
  937.         malloc(sizeof(int) * w->mlink.tileFaces)))
  938.     XtError("Not enough memory, exiting.");
  939.   if (startPosition)
  940.     (void) free((void *) startPosition);
  941.   if (!(startPosition = (int *)
  942.         malloc(sizeof(int) * w->mlink.tileFaces)))
  943.     XtError("Not enough memory, exiting.");
  944.   w->mlink.spacePosition = w->mlink.tileFaces - 1;
  945.   w->mlink.tileOfPosition[w->mlink.tileFaces - 1] = 0;
  946.   for (i = 1; i < w->mlink.tileFaces; i++)
  947.     w->mlink.tileOfPosition[i - 1] = i;
  948.   FlushMoves(w);
  949.   w->mlink.started = FALSE;
  950. }
  951.  
  952. static void ResizeTiles(w)
  953.   MlinkWidget w;
  954. {
  955.   w->mlink.digitOffset.x = 3;
  956.   w->mlink.digitOffset.y = 4;
  957. }
  958.  
  959. static void MoveNoTiles(w)
  960.   MlinkWidget w;
  961. {
  962.   mlinkCallbackStruct cb;
  963.  
  964.   cb.reason = MLINK_IGNORE;
  965.   XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  966. }
  967.  
  968. static int MoveTilesDir(w, direction, tile)
  969.   MlinkWidget w;
  970.   int direction, tile;
  971. {
  972.   switch (direction) {
  973.     case TOP:
  974.       RotateTiles(w, tile, TOP);
  975.       return TRUE;
  976.     case RIGHT:
  977.       if (Row(w, w->mlink.spacePosition) > 0) {
  978.         MoveTiles(w, w->mlink.spacePosition - 1);
  979.         return TRUE;
  980.       }
  981.       break;
  982.     case BOTTOM:
  983.       RotateTiles(w, tile, BOTTOM);
  984.       return TRUE;
  985.     case LEFT:
  986.       if (Row(w, w->mlink.spacePosition) < w->mlink.tiles - 1) {
  987.         MoveTiles(w, w->mlink.spacePosition + 1);
  988.         return TRUE;
  989.       }
  990.       break;
  991.     default:
  992.       (void) printf("MoveTilesDir: direction %d\n", direction);
  993.   }
  994.   return FALSE;
  995. }
  996.  
  997. static void RandomizeTiles(w)
  998.   MlinkWidget w;
  999. {
  1000.   mlinkCallbackStruct cb;
  1001.  
  1002.   /* First interchange tiles an even number of times */
  1003.   if (w->mlink.tiles > 1 && w->mlink.faces > 1 && w->mlink.tileFaces > 4) {
  1004.     int pos, count = 0;
  1005.  
  1006.     for (pos = 0; pos < w->mlink.tileFaces; pos++) {
  1007.        int randomPos = pos;
  1008.  
  1009.        while (randomPos == pos)
  1010.          randomPos = NRAND(w->mlink.tileFaces);
  1011.        count += ExchangeTiles(w, pos, randomPos);
  1012.     }
  1013.     if (count % 2)
  1014.       if (!ExchangeTiles(w, 0, 1))
  1015.         if (!ExchangeTiles(w,
  1016.               w->mlink.tileFaces - 2, w->mlink.tileFaces - 1))
  1017.           (void) printf("RandomizeTiles: should not get here\n");
  1018.     DrawAllTiles(w, w->mlink.tileGC, w->mlink.borderGC);
  1019.   }
  1020.   /* Now move the space around randomly */
  1021.   if (w->mlink.tiles > 1 || w->mlink.faces > 1) {
  1022.     int big = w->mlink.tileFaces + NRAND(2);
  1023.     int lastDirection = 0;
  1024.     int randomDirection;
  1025.  
  1026.     cb.reason = MLINK_RESET;
  1027.     XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  1028.  
  1029. #ifdef DEBUG
  1030.     big = 3;
  1031. #endif
  1032.     if (big > 100)
  1033.       big = 100;
  1034.     while (big--) {
  1035.       randomDirection = NRAND(COORD);
  1036.  
  1037. #ifdef DEBUG
  1038.       sleep(1);
  1039. #endif
  1040.  
  1041.       if ((randomDirection + COORD / 2) % COORD != lastDirection ||
  1042.           (w->mlink.tiles == 1 && w->mlink.faces == 1)) {
  1043.         if (MoveMlink(w, randomDirection, NRAND(w->mlink.tiles), FALSE))
  1044.           lastDirection = randomDirection;
  1045.         else
  1046.           big++;
  1047.       }
  1048.     }
  1049.     FlushMoves(w);
  1050.     cb.reason = MLINK_RANDOMIZE;
  1051.     XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  1052.   }
  1053.   if (CheckSolved(w)) {
  1054.     cb.reason = MLINK_SOLVED;
  1055.     XtCallCallbacks((Widget) w, XtNselectCallback, &cb);
  1056.   }
  1057. }
  1058.  
  1059. static void MoveTiles(w, from)
  1060.   MlinkWidget w;
  1061.   int from;
  1062. {
  1063.   int tempTile;
  1064.  
  1065.   tempTile = w->mlink.tileOfPosition[from];
  1066.   w->mlink.tileOfPosition[from] =
  1067.     w->mlink.tileOfPosition[w->mlink.spacePosition];
  1068.   w->mlink.tileOfPosition[w->mlink.spacePosition] = tempTile;
  1069.   DrawTile(w, w->mlink.tileGC, w->mlink.borderGC, w->mlink.spacePosition,
  1070.     FALSE);
  1071.   w->mlink.spacePosition = from;
  1072.   DrawTile(w, w->mlink.inverseGC, w->mlink.inverseGC, w->mlink.spacePosition,
  1073.     FALSE);
  1074. }
  1075.  
  1076. static void RotateTiles(w, c, direction)
  1077.   MlinkWidget w;
  1078.   int c, direction;
  1079. {
  1080.   int newR, k, tempTile, currTile, pos, newPos;
  1081.   int r = 0, space = -1, loop = FALSE;
  1082.  
  1083.   if (w->mlink.middle && c > 0 && c < w->mlink.tiles - 1) {
  1084.     loop = TRUE;
  1085.     c = 1;
  1086.   }
  1087.   do {
  1088.     currTile = w->mlink.tileOfPosition[r * w->mlink.tiles + c];
  1089.     for (k = 1; k <= w->mlink.faces; k++) {
  1090.       newR = (direction == TOP) ? (r + w->mlink.faces - 1) % w->mlink.faces:
  1091.         (r + 1) % w->mlink.faces;
  1092.       pos = r * w->mlink.tiles + c;
  1093.       newPos = newR * w->mlink.tiles + c;
  1094.       tempTile = w->mlink.tileOfPosition[newPos];
  1095.       w->mlink.tileOfPosition[newPos] = currTile;
  1096.       currTile = tempTile;
  1097.       if (w->mlink.spacePosition == pos) {
  1098.         DrawTile(w, w->mlink.inverseGC, w->mlink.inverseGC, newPos, FALSE);
  1099.         space = newPos;
  1100.       } else
  1101.         DrawTile(w, w->mlink.tileGC, w->mlink.borderGC, newPos, FALSE);
  1102.       r = newR;
  1103.     }
  1104.     if (space >= 0)
  1105.       w->mlink.spacePosition = space;
  1106.   } while (loop && ++c < w->mlink.tiles - 1);
  1107. }
  1108.  
  1109. static int ExchangeTiles(w, pos1, pos2)
  1110.   MlinkWidget w;
  1111.   int pos1, pos2;
  1112. {
  1113.   int tempTile;
  1114.  
  1115.   if (w->mlink.tileOfPosition[pos1] <= 0)
  1116.     return FALSE;
  1117.   else if (w->mlink.tileOfPosition[pos2] <= 0)
  1118.     return FALSE;
  1119.   tempTile = w->mlink.tileOfPosition[pos1];
  1120.   w->mlink.tileOfPosition[pos1] = w->mlink.tileOfPosition[pos2];
  1121.   w->mlink.tileOfPosition[pos2] = tempTile;
  1122.   return TRUE;
  1123. }
  1124.  
  1125. static void DrawFrame(w, gc)
  1126.   MlinkWidget w;
  1127.   GC gc;
  1128. {
  1129.   int sumX, sumY, offsetX, offsetY, r;
  1130.  
  1131.   sumX = w->mlink.tiles * w->mlink.offset.x + w->mlink.delta.x / 2 + 1;
  1132.   sumY = w->mlink.offset.y;
  1133.   offsetX = w->mlink.puzzleOffset.x;
  1134.   offsetY = w->mlink.puzzleOffset.y;
  1135.   for (r = 0; r < w->mlink.faces; r++) {
  1136.     XFillRectangle(XtDisplay(w), XtWindow(w), gc, 
  1137.       offsetX, offsetY, 1, sumY);
  1138.     XFillRectangle(XtDisplay(w), XtWindow(w), gc,
  1139.       offsetX, offsetY, sumX, 1);
  1140.     XFillRectangle(XtDisplay(w), XtWindow(w), gc,
  1141.       sumX + offsetX, offsetY, 1, sumY + 1);
  1142.     XFillRectangle(XtDisplay(w), XtWindow(w), gc,
  1143.       offsetX, sumY + offsetY, sumX + 1, 1);
  1144.     offsetY += sumY;
  1145.   }
  1146. }   
  1147.  
  1148. void DrawAllTiles(w, tileGC, borderGC)
  1149.   MlinkWidget w;
  1150.   GC tileGC, borderGC;
  1151. {
  1152.   int k;
  1153.  
  1154.   for (k = 0; k < w->mlink.tileFaces; k++)
  1155.     if (w->mlink.tileOfPosition[k] <= 0)
  1156.       DrawTile(w, w->mlink.inverseGC, w->mlink.inverseGC, k, FALSE);
  1157.     else
  1158.       DrawTile(w, tileGC, borderGC, k, FALSE);
  1159. }
  1160.  
  1161. static void DrawTile(w, tileGC, borderGC, pos, offset)
  1162.   MlinkWidget w;
  1163.   GC tileGC, borderGC;
  1164.   int pos, offset;
  1165. {
  1166.   int dx, dy, tile = w->mlink.tileOfPosition[pos];
  1167.  
  1168.   dx = Row(w, pos) * w->mlink.offset.x + w->mlink.delta.x +
  1169.     w->mlink.puzzleOffset.x + offset;
  1170.   dy = Column(w, pos) * w->mlink.offset.y + w->mlink.delta.y +
  1171.     w->mlink.puzzleOffset.y + offset;
  1172.   XFillRectangle(XtDisplay(w), XtWindow(w), tileGC, dx, dy,
  1173.     w->mlink.tileSize.x, w->mlink.tileSize.y - 2);
  1174.   if (tileGC != w->mlink.inverseGC) {
  1175.     int i = 0, offsetX = 0, t = tile, mono;
  1176.     char buf[6];
  1177.  
  1178.     mono = (w->mlink.depth == 1 || w->mlink.mono);
  1179.     if (w->mlink.orient || mono) {
  1180.       if (mono) {
  1181.         if (w->mlink.orient) {
  1182.           (void) sprintf(buf, "%d%c", tile,
  1183.                    w->mlink.faceName[((tile - 1) / w->mlink.tiles + 1) %
  1184.                      w->mlink.faces][0]);
  1185.           t *= w->mlink.base;
  1186.         } else {
  1187.           (void) sprintf(buf, "%c", w->mlink.faceName[((tile - 1) /
  1188.                    w->mlink.tiles + 1) % w->mlink.faces][0]);
  1189.           t = 1;
  1190.         }
  1191.       } else
  1192.         (void) sprintf(buf, "%d", tile);
  1193.       while (t >= 1) {
  1194.         t /= w->mlink.base;
  1195.         offsetX += w->mlink.digitOffset.x;
  1196.         i++;
  1197.       }
  1198.       XDrawString(XtDisplay(w), XtWindow(w),
  1199.         w->mlink.faceGC[((tile - 1) / w->mlink.tiles + 1) % w->mlink.faces],
  1200.         dx + w->mlink.tileSize.x / 2 - offsetX,
  1201.         dy + w->mlink.tileSize.y / 2 + w->mlink.digitOffset.y, buf, i);
  1202.     }
  1203.   }
  1204.   if (tile != 0 && 
  1205.       ((tile != w->mlink.tileFaces - 1 && w->mlink.tiles > 1) ||
  1206.        w->mlink.tiles > 2))
  1207.     DrawLink(w,
  1208.       w->mlink.faceGC[((tile - 1) / w->mlink.tiles + 1) % w->mlink.faces],
  1209.       pos, offset);
  1210.   XDrawRectangle(XtDisplay(w), XtWindow(w), borderGC, dx, dy,
  1211.     w->mlink.tileSize.x, w->mlink.tileSize.y - 2);
  1212. }
  1213.  
  1214. #define MULT 64
  1215. #define THICKNESS 7 
  1216. static void DrawLink(w, gc, pos, offset)
  1217.   MlinkWidget w;
  1218.   GC gc;
  1219.   int pos, offset;
  1220. {
  1221.   int dx, dy, sizex, sizey, tile = w->mlink.tileOfPosition[pos], thick;
  1222.  
  1223.   sizex = w->mlink.offset.x * 3 / 2 - 3;
  1224.   sizey = w->mlink.offset.y * 3 / 4 - 3;
  1225.   dx = Row(w, pos) * w->mlink.offset.x + w->mlink.delta.x +
  1226.     w->mlink.puzzleOffset.x - sizex / 2 + offset;
  1227.   dy = Column(w, pos) * w->mlink.offset.y + w->mlink.delta.y +
  1228.     w->mlink.puzzleOffset.y + w->mlink.tileSize.y / 2 - sizey / 2 - 1 + offset;
  1229.   thick = MIN(sizey, sizex) / 3;
  1230.   thick = MIN(thick, THICKNESS);
  1231.   if (thick > 1) {
  1232.     XSetLineAttributes(XtDisplay(w), gc, thick,
  1233.       LineSolid, CapNotLast, JoinRound);
  1234.     if (tile % w->mlink.tiles == 0 || tile == w->mlink.tileFaces - 1)
  1235.       XDrawArc(XtDisplay(w), XtWindow(w), gc, dx, dy,
  1236.         sizex, sizey, 89 * MULT, -179 * MULT);
  1237.     else if (tile % w->mlink.tiles == 1)
  1238.       XDrawArc(XtDisplay(w), XtWindow(w), gc, dx + w->mlink.tileSize.x - 1, dy,
  1239.         sizex, sizey, 90 * MULT, 180 * MULT);
  1240.     else {
  1241.       XDrawArc(XtDisplay(w), XtWindow(w), gc, dx, dy,
  1242.         sizex, sizey, 89 * MULT, -124 * MULT);
  1243.       XDrawArc(XtDisplay(w), XtWindow(w), gc, dx, dy,
  1244.         sizex, sizey, -90 * MULT, 30 * MULT);
  1245.       dx += w->mlink.tileSize.x - 1;
  1246.       XDrawArc(XtDisplay(w), XtWindow(w), gc, dx, dy,
  1247.         sizex, sizey, 90 * MULT, 30 * MULT);
  1248.       XDrawArc(XtDisplay(w), XtWindow(w), gc, dx, dy,
  1249.         sizex, sizey, -90 * MULT, -125 * MULT);
  1250.     }
  1251.     XSetLineAttributes(XtDisplay(w), gc, 1, LineSolid, CapNotLast, JoinRound);
  1252.   }
  1253. }
  1254. /*
  1255. #define MULT 64
  1256. #define THICKNESS 8 
  1257. static void DrawLink(w, gc, pos, offset)
  1258.   MlinkWidget w;
  1259.   GC gc;
  1260.   int pos;
  1261. {
  1262.   int dx, dy, sizex, sizey, i, tile = w->mlink.tileOfPosition[pos];
  1263.  
  1264.   for (i = 0; i < THICKNESS; i++) {
  1265.     sizex = w->mlink.offset.x * 3 / 2 - i;
  1266.     sizey = w->mlink.offset.y * 3 / 4 - i;
  1267.     dx = Row(w, pos) * w->mlink.offset.x + w->mlink.delta.x +
  1268.       w->mlink.puzzleOffset.x - sizex / 2 + offset;
  1269.     dy = Column(w, pos) * w->mlink.offset.y + w->mlink.delta.y +
  1270.       w->mlink.puzzleOffset.y + w->mlink.tileSize.y / 2 - sizey / 2 + offset;
  1271.     if (tile % w->mlink.tiles == 0 || tile == w->mlink.tileFaces - 1)
  1272.       XDrawArc(XtDisplay(w), XtWindow(w), gc, dx, dy,
  1273.         sizex, sizey, 89 * MULT, -179 * MULT);
  1274.     else if (tile % w->mlink.tiles == 1)
  1275.       XDrawArc(XtDisplay(w), XtWindow(w), gc, dx + w->mlink.tileSize.x - 1, dy,
  1276.         sizex, sizey, 90 * MULT, 180 * MULT);
  1277.     else {
  1278.       XDrawArc(XtDisplay(w), XtWindow(w), gc, dx, dy,
  1279.         sizex, sizey, 89 * MULT, -32 * MULT);
  1280.       XDrawArc(XtDisplay(w), XtWindow(w), gc, dx, dy,
  1281.         sizex, sizey, -90 * MULT, 128 * MULT);
  1282.       dx += w->mlink.tileSize.x - 1;
  1283.       XDrawArc(XtDisplay(w), XtWindow(w), gc, dx, dy,
  1284.         sizex, sizey, 90 * MULT, 128 * MULT);
  1285.       XDrawArc(XtDisplay(w), XtWindow(w), gc, dx, dy,
  1286.         sizex, sizey, -90 * MULT, -33 * MULT);
  1287.     }
  1288.   }
  1289. }
  1290. */
  1291. static int PositionTile(w, x, r)
  1292.   MlinkWidget w;
  1293.   int x;
  1294.   int *r;
  1295. {
  1296.   *r = (x - w->mlink.delta.x / 2 - w->mlink.puzzleOffset.x) / w->mlink.offset.x;
  1297.   if (*r < 0 || *r >= w->mlink.tiles)
  1298.     return FALSE;
  1299.   return TRUE;
  1300. }
  1301.  
  1302. static int Row(w, pos)
  1303.   MlinkWidget w;
  1304.   int pos;
  1305. {
  1306.   return (pos % w->mlink.tiles);
  1307. }
  1308.  
  1309. static int Column(w, pos)
  1310.   MlinkWidget w;
  1311.   int pos;
  1312. {
  1313.   return (pos / w->mlink.tiles);
  1314. }
  1315.  
  1316. Boolean CheckSolved(w)
  1317.   MlinkWidget w;
  1318. {
  1319.   int i, j, firstTile, lastTile, midTile;
  1320.   if (w->mlink.tiles < 2)
  1321.     return TRUE;
  1322.   if (w->mlink.orient) {
  1323.     firstTile = w->mlink.tileOfPosition[0];
  1324.     if (firstTile != 0 && (firstTile - 1) % w->mlink.tiles == 0) {
  1325.       for (i = 1; i < w->mlink.tileFaces; i++) {
  1326.         midTile = w->mlink.tileOfPosition[i];
  1327.         if (midTile && (midTile - firstTile + w->mlink.tileFaces) %
  1328.             w->mlink.tileFaces != i)
  1329.           return FALSE;
  1330.       }
  1331.     } else
  1332.       return FALSE;
  1333.   } else {
  1334.     for (i = 0; i < w->mlink.faces; i++) {
  1335.       firstTile = w->mlink.tileOfPosition[i * w->mlink.tiles];
  1336.       if (firstTile == 0)
  1337.         firstTile = w->mlink.tileOfPosition[i * w->mlink.tiles + 1];
  1338.       lastTile = w->mlink.tileOfPosition[(i + 1) * w->mlink.tiles - 1];
  1339.       if (lastTile == 0)
  1340.         lastTile = w->mlink.tileOfPosition[(i + 1) * w->mlink.tiles - 2];
  1341.       if (firstTile % w->mlink.tiles != 1 ||
  1342.           (lastTile % w->mlink.tiles != 0 &&
  1343.            lastTile != w->mlink.tileFaces - 1) ||
  1344.           (lastTile - 1) / w->mlink.tiles !=
  1345.           (firstTile - 1) / w->mlink.tiles)
  1346.         return FALSE;
  1347.       for (j = 1; j < w->mlink.tiles - 1; j++) {
  1348.         midTile = w->mlink.tileOfPosition[i * w->mlink.tiles + j];
  1349.         if ((midTile - 1) / w->mlink.tiles !=
  1350.             (firstTile - 1) / w->mlink.tiles || midTile == 0)
  1351.           return FALSE;
  1352.       }
  1353.     }
  1354.   }
  1355.   return TRUE;
  1356. }
  1357.